#include "ComboSystem.h"
#include "ResourceManagers.h"

#include <cstdio>
using namespace std;

// valores que definen el sistema de combos
float keyVel=100;                 // velocidad con la que las teclas van hacia la izquierda
float timeBetweenKeyStrokes=1;    // cantidad de segundos antes de que salga otra tecla
float goodHitMaxPoints=10;            // cantidad de puntos maximos que puede dar por un golpe
float badHitPoints=-20;               // lo que saca errar (todos los errores sacan igual)
float minHitDistance=30;          // distancia minima en pixeles que se considera valida para decir que se le pego a la tecla
float perfectDistance=5;         // si el hit ocurre en una distancia menor a esta se considera perfecto
float timeTillFirstStroke=0.7;         // tiempo que espera despues de que el jugador entra en modo hatch para tirar el primer keystroke



ComboSystem::ComboSystem(){
    m_active=false;
    m_nest=Nest::Instance();
    timeToNextKeystroke=timeTillFirstStroke;
    //sf::Image *i=ImageManager::Instance()->GetImage("comboSystem.png");
	sf::Image *i=ImageManager::Instance()->GetImage("canal.png");
    m_systemSprite.SetImage(*i);
    m_systemSprite.SetCenter(i->GetWidth()/2, i->GetHeight()/2);
    m_bullsEyePos=m_nest->GetPosition();
    m_bullsEyePos.y-=50;
    m_systemSprite.SetPosition(m_bullsEyePos);
	niceSound.SetBuffer(*SoundBufferManager::Instance()->GetBuffer("combo_bien.wav"));
	badSound.SetBuffer(*SoundBufferManager::Instance()->GetBuffer("combo_mal.wav"));

}


#include <cstdio>
using namespace std;
void ComboSystem::Update(float dt){
    if(!m_active) return;
//    printf("updating...\n");
    // si es tiempo de agregar otra keystroke, la agrega
    // si no, actualiza el tiempo
    if(timeToNextKeystroke<=0){
        timeToNextKeystroke=timeBetweenKeyStrokes;
        KeyStroke newKeyStroke;
        newKeyStroke.SetPosition(m_bullsEyePos.x+m_systemSprite.GetImage()->GetWidth()/2, m_bullsEyePos.y);
        m_keystrokes.push_back(newKeyStroke);
    }else{
        timeToNextKeystroke-=dt;
    }

    // busca la keystroke mas cercana
    int nextKeyStroke=GetNearest();
    // si nada se presiono todavia para la siguiente keystroke
    if(nextKeyStroke==-1) return;
    if(m_keystrokes[nextKeyStroke].m_status==KeyStroke::NONE){
        // revisamos los controles
        bool somethingPressed=false;            // si se presiono algo
        PlayerControl::Controls whatIsPressed;  // que es lo que se presiono
        if(m_control->ControlPressed(PlayerControl::CONTROL_UP)){
            somethingPressed=true;
            whatIsPressed=PlayerControl::CONTROL_UP;
        }else if(m_control->ControlPressed(PlayerControl::CONTROL_DOWN)){
            somethingPressed=true;
            whatIsPressed=PlayerControl::CONTROL_DOWN;
        }else if(m_control->ControlPressed(PlayerControl::CONTROL_RIGHT)){
            somethingPressed=true;
            whatIsPressed=PlayerControl::CONTROL_RIGHT;
        }else if(m_control->ControlPressed(PlayerControl::CONTROL_LEFT)){
            somethingPressed=true;
            whatIsPressed=PlayerControl::CONTROL_LEFT;
        }

        // si se presiono algo
        if(somethingPressed){
            // revisamos que la tecla persionada sea la correcta
            if(whatIsPressed==m_keystrokes[nextKeyStroke].m_key){
                m_keystrokes[nextKeyStroke].m_status=KeyStroke::CORRECT;
                float dist=fabs(m_keystrokes[nextKeyStroke].GetPosition().x-m_bullsEyePos.x);
                if(dist>minHitDistance){
					badSound.Play();
                    //printf("MAL\n");
                    m_nest->AddTemp(badHitPoints);
                    m_keystrokes[nextKeyStroke].m_status=KeyStroke::FAILED;
                }else if(dist<perfectDistance){
					niceSound.Play();
                    //printf("PERFECTO\n");
                    m_nest->AddTemp(goodHitMaxPoints);
                    m_keystrokes[nextKeyStroke].m_status=KeyStroke::CORRECT;
                }else{
					niceSound.Play();
                    // la cantidad de puntos es inversamente proporcional a la distancia
                    float puntos=goodHitMaxPoints*(1-((dist-perfectDistance)/(minHitDistance-perfectDistance)));
                    m_nest->AddTemp(puntos);
                    //printf("BIEN\n");
                    m_keystrokes[nextKeyStroke].m_status=KeyStroke::CORRECT;
                }

            }else{
				badSound.Play();
                m_keystrokes[nextKeyStroke].m_status=KeyStroke::FAILED;
                //printf("MAL\n");
                m_nest->AddTemp(badHitPoints);
            }
        }
    }


    vector<KeyStroke>::iterator p=m_keystrokes.begin();
    while(p!=m_keystrokes.end()){
        // mueve las keystrokes
        p->Move(-keyVel*dt,0);

        // si hay alguna que ya haya salido del sistema de combos, la quita
        if(p->GetPosition().x<m_bullsEyePos.x-+m_systemSprite.GetImage()->GetWidth()/2){
            // si la tecla llego al final sin ser presionada, restamos puntos antes de borrarla
            if(p->m_status==KeyStroke::NONE){
				badSound.Play();
                //printf("MAL\n");
                m_nest->AddTemp(badHitPoints);
            }
            p=m_keystrokes.erase(p);
        }else p++;
    }
}

void ComboSystem::Draw(sf::RenderWindow *wnd){
    if(!m_active) return;
    sf::Sprite keystrokeStatus;
	wnd->Draw(m_systemSprite);
    for(unsigned i=0; i<m_keystrokes.size(); i++){
        wnd->Draw(m_keystrokes[i]);
        if(m_keystrokes[i].m_status==KeyStroke::CORRECT){
            keystrokeStatus=m_keystrokes[i];
			keystrokeStatus.SetImage(*ImageManager::Instance()->GetImage("combo_good.png"));
			keystrokeStatus.SetSubRect(IntRect(0,0,keystrokeStatus.GetImage()->GetWidth(),keystrokeStatus.GetImage()->GetWidth()));
			keystrokeStatus.SetCenter(keystrokeStatus.GetImage()->GetWidth()/2.0f,keystrokeStatus.GetImage()->GetHeight()/2.0f);



            wnd->Draw(keystrokeStatus);
        }else if(m_keystrokes[i].m_status==KeyStroke::FAILED){
            keystrokeStatus=m_keystrokes[i];
			keystrokeStatus.SetImage(*ImageManager::Instance()->GetImage("combo_bad.png"));
			keystrokeStatus.SetSubRect(IntRect(0,0,keystrokeStatus.GetImage()->GetWidth(),keystrokeStatus.GetImage()->GetWidth()));
			keystrokeStatus.SetCenter(keystrokeStatus.GetImage()->GetWidth()/2.0f,keystrokeStatus.GetImage()->GetHeight()/2.0f);
//			keystrokeStatus.SetColor(Color::Red);

            wnd->Draw(keystrokeStatus);
        }
	}

}


int ComboSystem::GetNearest(){
    float minDist=1000, dist;
    unsigned size=m_keystrokes.size();
    unsigned iMin=-1;
    if(size==1) return 0;

    for(unsigned i=0; i<size; i++){
        dist=fabs(m_keystrokes[i].GetPosition().x-m_bullsEyePos.x);
        if(dist<=minDist){
            minDist=dist;
            iMin=i;
        }
    }

    return iMin;
}


ComboSystem::KeyStroke::KeyStroke(){
    m_status=NONE;
    //SetImage(*ImageManager::Instance()->GetImage("arrow.png"));
    SetCenter(8,8);
    switch(rand()%4){
        case 0:
			SetImage(*ImageManager::Instance()->GetImage("arr1.png"));
            m_key=PlayerControl::CONTROL_UP;
            //Rotate(90);
        break;

        case 1:
			SetImage(*ImageManager::Instance()->GetImage("aba1.png"));
            m_key=PlayerControl::CONTROL_DOWN;
            //Rotate(-90);
        break;

        case 2:
			SetImage(*ImageManager::Instance()->GetImage("izq1.png"));
            m_key=PlayerControl::CONTROL_LEFT;
            //Rotate(180);
        break;

        case 3:
			SetImage(*ImageManager::Instance()->GetImage("dere1.png"));
            m_key=PlayerControl::CONTROL_RIGHT;

        break;
    }
}

void ComboSystem::Activate(PlayerControl *control){
    m_active=true;
    m_control=control;
    timeToNextKeystroke=timeTillFirstStroke;
    m_keystrokes.clear();
}

void ComboSystem::Deactivate(){
    m_active=false;
}

ComboSystem *ComboSystem::Instance(){
    static ComboSystem instance;
    return &instance;
}

void ComboSystem::IncrementDifficulty(){
    // aumenta la velocidad de las keystrokes
    keyVel*=1.05;
    // aumenta la frecuencia de las keystrokes
    timeBetweenKeyStrokes*=0.95;
    // disminuye la distancia a la cual un golpe es bueno
    minHitDistance*=0.97;
}
